//
//  Notifier.swift
//  AG
//
//  Created by zhuxuhong on 2020/11/18.
//  Copyright © 2020 AgoCulture. All rights reserved.
//

import Foundation

final public class Notifier {
    public typealias Handler = ((Notification)->Void)
    static let shared = Notifier()
    
    public struct Observer {
        var name: Notification.Name!
        var object: Any?
        weak var target: NSObject?
        var handler: Handler!
        init(name: Notification.Name, object: Any?, target: NSObject, handler: @escaping Handler) {
            self.name = name
            self.object = object
            self.target = target
            self.handler = handler
        }
    }
    
    private var observers: [Observer] = []
    
    public static func remove(_ name: Notification.Name, for target: NSObject) {
        remove([name], for: target)
    }
    
    public static func removeAll(for target: AnyObject) {
        shared.observers.removeAll { v -> Bool in
            guard let t =  v.target else {
                NotificationCenter.default.removeObserver(shared, name: v.name, object: v.object)
                return true
            }
            if _addressOf(object: t) == _addressOf(object: target) {
                NotificationCenter.default.removeObserver(shared, name: v.name, object: v.object)
                return true
            }
            return false
        }
    }
    
    private static func _addressOf(object: AnyObject) -> String {
        let v = Unmanaged<AnyObject>.passUnretained(object).toOpaque()
        return String(describing: v)
    }

    public static func remove(_ names: [Notification.Name], for target: NSObject) {
        names.forEach{ name in
            shared.observers.removeAll { v -> Bool in
                guard let t =  v.target else {
                    NotificationCenter.default.removeObserver(shared, name: v.name, object: v.object)
                    return true
                }
                if v.name == name,
                    t == target {
                    NotificationCenter.default.removeObserver(shared, name: v.name, object: v.object)
                    return true
                }
                return false
            }
        }
    }
    
    public static func post(_ name: Notification.Name, object: Any? = nil, info: [AnyHashable : Any]? = nil) {
        NotificationCenter.default.post(name: name, object: object, userInfo: info)
    }
    
    public static func observe(_ names: [Notification.Name], on target: NSObject, object: Any? = nil, handler: @escaping Handler) {
        names.forEach {
            Self.observe($0, on: target, object: object, handler: handler)
        }
    }
    
    static func observe(_ name: Notification.Name, on target: NSObject, object: Any? = nil, handler: @escaping Handler) {
        shared.observers.append(.init(name: name, object: object, target: target, handler: handler))
        NotificationCenter.default.addObserver(shared, selector: #selector(_notified(_:)), name: name, object: object)
    }
    
    @objc private func _notified(_ sender: Notification) {
        observers.forEach {
            if $0.name == sender.name {
                $0.handler(sender)
            }
        }
    }
    
    init(){}
}
